module Maze
  module PngFormatter
    class Orthogonal
      include DrawingHelpers

      attr_reader :maze, :image, :lines

      def initialize(maze)
        @maze    = maze
        @image   = ChunkyPNG::Image.new(width, height, background_colour)
        @lines   = []

        draw_image
      end

      private

      def width
        (maze.width * LENGTH + padding_offset).to_i
      end

      def height
        (maze.height * LENGTH + padding_offset).to_i
      end

      def origins
        @origins ||= compute_origin_points
      end

      def compute_origin_points
        points = Array.new(maze.height) { Array.new(maze.width) }

        maze.height.times do |row|
          maze.width.times do |column|
            points[row][column] =
              if 0 == column
                first_origin_point(row)
              else
                next_horizontal_origin_point(points[row][column - 1])
              end
          end
        end

        points
      end

      def first_origin_point(for_row)
        x = (PADDING + LENGTH * 0.5).to_i
        y = (PADDING + LENGTH * 0.5 + for_row * LENGTH).to_i

        Point.new(x, y)
      end

      def next_horizontal_origin_point(previous_point)
        previous_point.update { |next_origin| next_origin.x += LENGTH }
      end

      #
      # Given the origin computes the corner points of the square
      #
      #    1                 2
      #    <--------L-------->
      #    +--------+--------+
      #    |      ^ |<-.5 L->|
      #    |     .5L|        |
      #    |      V |        |
      #    +--------+--------+
      #    |        |O       |
      #    |        |        |
      #    |        |        |
      #    +--------+--------+
      #    4                 3
      #
      # O is Origin
      #
      # Faces in clockwise direction:
      #   * North : 1 - 2
      #   * East  : 2 - 3
      #   * South : 3 - 4
      #   * West  : 4 - 1
      #
      def corner_points(origin)
        faces   = []
        offsets = [ [-LENGTH, -LENGTH],
                    [ LENGTH, -LENGTH],
                    [ LENGTH,  LENGTH],
                    [-LENGTH,  LENGTH],
                    [-LENGTH, -LENGTH] ]

        offsets.each do |(offset_x, offset_y)|
          faces << origin.update do |corner|
            corner.x += offset_x / 2
            corner.y += offset_y / 2
          end
        end
        faces
      end

      #
      # Group adjacent corner points into pairs
      #
      def face_line_segment(origin)
        corner_points(origin).each_cons(2)
      end

      def clockwise_directions
        [ maze.class::N, maze.class::E, maze.class::S, maze.class::W ]
      end

      def filter_open_faces(line_segments, open = [])
        line_segments.reject.each_with_index do |segment, i|
          open.include? clockwise_directions[i]
        end
      end

      def closed_line_segments(row, column)
        filter_open_faces( face_line_segment(origins[row][column]),
                           maze.grid[row][column] )
      end
    end
  end
end

